/******************************************************************************
*	cmCond.c	Code file of CM condition Module.
*
*	coypright (C) 2009 Ding Lixing
*
******************************************************************************/

#include "cm.h"

extern cmTASK *cmTaskGetCurrent(void);

enum cmCondOpration_enu {
    OP_NULL,
    OP_AND,
    OP_OR,
    OP_CLR
};
typedef enum cmCondOpration_enu cmCONDOP;

struct cmCondParam_typ {
    cmCONDOP        op;
    unsigned long   flag;
};
typedef struct cmCondParam_typ cmCONDPARAM;


static int condPrePost(cmOBJ *obj, void *param)
{
    cmCONDPARAM *cParam = (cmCONDPARAM *)param;
    cmCONDDATA  *data   = (cmCONDDATA *)(obj->data);

    switch(cParam->op){
        case OP_AND:
            data->flag &= cParam->flag;
            break;
        case OP_OR:
            data->flag |= cParam->flag;
            break;
        default:
            return CM_ERR_CoreFail;
    }

    return 0;
}

static int condPostEachCallback(cmOBJ *obj, cmTASK *pendTask)
{
    cmCONDDATA  *data   = (cmCONDDATA *)(obj->data);
    unsigned long flag;

    flag = pendTask->condWait & data->flag;
    if((flag == pendTask->condWait) || ((pendTask->condType & CM_COND_WAIT_ANY) && (flag != 0))){
        return 0;
    }
    return -1;
}

static int condPrePend(cmOBJ *obj, void *param)
{
    cmCONDPARAM *cParam = (cmCONDPARAM *)param;
    cmCONDDATA  *data   = (cmCONDDATA *)(obj->data);
    cmTASK      *self   = cmTaskGetCurrent();
    unsigned long flag;

    flag = self->condWait & data->flag;
    
    if((flag == cParam->flag) || ((self->condType & CM_COND_WAIT_ANY) && (flag != 0))){
        if(self->condType & CM_COND_WAIT_CLR){
            data->flag &= ~flag;
        }
        return 0;
    }

    return CM_ERR_CondNotMatch;
}

int cmCondInit(cmCOND *cond, unsigned int opt, unsigned long flag)
{
    cond->data.flag = flag;
    return cmTaskObjInit(&cond->obj, CM_OBJ_COND, opt, condPrePost, condPrePend, (void*)&cond->data);
}

int cmCondClr(cmCOND *cond, unsigned long flag)
{
    cmCONDPARAM cParam;

    cParam.op = OP_AND;
    cParam.flag = ~flag;
    return cmTaskObjPostEach(&cond->obj, (void*)&cParam, condPostEachCallback);
}

int cmCondSet(cmCOND *cond, unsigned long flag)
{
    cmCONDPARAM cParam;

    cParam.op = OP_OR;
    cParam.flag = flag;
    return cmTaskObjPostEach(&cond->obj, (void*)&cParam, condPostEachCallback);
}

static cmCondOptWait(
    cmCOND *cond,
    unsigned char type,
    unsigned long flag,
    unsigned int tout
)
{
    vmINTKEY key;
    cmTASK *self;
    cmCONDPARAM cParam;

    cParam.flag = flag;

    key = vmIntLock();
    self = cmTaskGetCurrent();
    self->condType = type;
    self->condWait = flag;
    vmIntUnlock(key);
    
    return cmTaskObjPend(&cond->obj, tout, (void*)&cParam);
}

int cmCondWaitAny(cmCOND *cond, unsigned long flag, unsigned int tout)
{
    return cmCondOptWait(cond, CM_COND_WAIT_ANY, flag, tout);
}

int cmCondWaitAll(cmCOND *cond, unsigned long flag, unsigned int tout)
{
    return cmCondOptWait(cond, CM_COND_WAIT_ALL, flag, tout);
}

int cmCondWaitAnyClean(cmCOND *cond, unsigned long flag, unsigned int tout)
{
    return cmCondOptWait(cond, CM_COND_WAIT_CLR | CM_COND_WAIT_ANY, flag, tout);
}

int cmCondWaitAllClean(cmCOND *cond, unsigned long flag, unsigned int tout)
{
    return cmCondOptWait(cond, CM_COND_WAIT_CLR | CM_COND_WAIT_ALL, flag, tout);
}

